package com.frinder.javafx;

import com.frinder.astar.*;
import com.frinder.util.ThreadUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import static com.frinder.util.StringUtils.xy;

/**
 * @author frinder
 * @date 2018/10/12
 * @Description: ${todo}
 */
@Slf4j
public class Map {

    private final Color cellDefaultColor = Color.valueOf("#409EFF");
    private final Color selectedNodeColor = Color.valueOf("#DCDFE6");
    private final Color sourceNodeColor = Color.valueOf("#909399");
    private final Color destNodeColor = Color.valueOf("#67C23A");
    private final Color pathNodeColor = Color.valueOf("#E6A23C");
    private final Color PointNodeColor = Color.valueOf("#303133");
    private final String locationTextTemp = "当前位置：[%s:%s]";

    /**
     * 地图格子 与 Node 映射
     */
    private final java.util.Map<Rectangle, Node> mapCells = Maps.newConcurrentMap();

    /**
     * xy 与 地图格子映射
     */
    private final java.util.Map<String, Rectangle> xyCells = Maps.newConcurrentMap();

    /**
     * 障碍点
     */
    private List<Point> blocks = Lists.newArrayList(
            new Point.Builder().x(9).y(2).build(),
            new Point.Builder().x(9).y(3).build(),
            new Point.Builder().x(9).y(4).build(),
            new Point.Builder().x(9).y(5).build(),
            new Point.Builder().x(9).y(6).build(),
            new Point.Builder().x(3).y(2).build(),
            new Point.Builder().x(3).y(4).build(),
            new Point.Builder().x(3).y(6).build()
    );

    /**
     * 障碍点 k -> v
     */
    private java.util.Map<String, Point> blockMap = blocks.stream().collect(Collectors.toMap(Point::getName, s -> s));

    private final ExecutorService executor = ThreadUtils.getThreadPoolExecutor(3);


    private BorderPane mainPane;
    private GridPane topPane;
    private GridPane centerPane;
    private Label bottomText;

    private Rectangle selectedNode;
    private Rectangle sourceNode;
    private Rectangle destNode;
    private Label locationText;

    private int xWidth;
    private int yHeight;

    public Map(int xWidth, int yHeight) {
        this.xWidth = xWidth;
        this.yHeight = yHeight;
        initPoints();
        drawPane();
    }

    public Pane getPane() {
        return mainPane;
    }

    /**
     * 模拟 blocks
     */
    private void initPoints() {
        blocks.clear();
        blockMap.clear();
        List<Point> PointList = Lists.newArrayList();
        for (int y = 0; y < yHeight; y++) {
            for (int x = 0; x < xWidth; x++) {
                PointList.add(new Point.Builder().x(x).y(y).build());
            }
        }
        Random random = new Random();
        while (blocks.size() < (yHeight * xWidth * 0.1)) {
            Point Point = PointList.get(random.nextInt(PointList.size()));
            if (!blocks.contains(Point)) {
                blocks.add(Point);
            }
        }
        blockMap = blocks.stream().collect(Collectors.toMap(Point::getName, s -> s));
    }

    /**
     * 画面板
     */
    private void drawPane() {
        mainPane = new BorderPane();
        mainPane.setPadding(new Insets(5, 5, 5, 5));
        //
        topPane = new GridPane();
        topPane.setHgap(5D);
        topPane.setPrefHeight(40D);
        topPane.setAlignment(Pos.CENTER_LEFT);
        topPane.setStyle("-fx-font-size: 18px");
        mainPane.setTop(topPane);
        //
        centerPane = new GridPane();
        centerPane.setHgap(1D);
        centerPane.setVgap(1D);
        centerPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        centerPane.setAlignment(Pos.TOP_LEFT);
        mainPane.setCenter(centerPane);
        //
        bottomText = new Label();
        bottomText.setTextFill(Color.RED);
        mainPane.setBottom(bottomText);
        // 画地图格子
        drawMap();
        // 添加组件
        addTopNodes();
    }

    /**
     * 画地图格子
     */
    private void drawMap() {
        for (int y = 0; y < yHeight; y++) {
            for (int x = 0; x < xWidth; x++) {
                Rectangle cell = new Rectangle();
                cell.setWidth(20);
                cell.setHeight(20);
                cell.setFill(cellDefaultColor);
                String xy = xy(x, y);
                if (blockMap.containsKey(xy)) {
                    cell.setFill(PointNodeColor);
                }
                String text = String.format(locationTextTemp, x, y);
                cell.setOnMouseMoved(event -> locationText.setText(text));
                cell.setOnMouseClicked(event -> {
                    // Point区块不能选中
                    if (!blockMap.containsKey(xy)) {
                        if (null != selectedNode) {
                            selectedNode.setFill(cellDefaultColor);
                        }
                        selectedNode = cell;
                        selectedNode.setFill(selectedNodeColor);
                    }
                });
                centerPane.add(cell, x, y);
                mapCells.put(cell, new Node.Builder().point(new Point.Builder().x(x).y(y).build()).build());
                xyCells.put(xy, cell);
            }
        }
    }

    /**
     * top 部分添加 nodes
     */
    private void addTopNodes() {
        // 定义组件
        Button zoomInBtn = new Button("放大");
        Button zoomOutBtn = new Button("缩小");
        Button sourceBtn = new Button("起点");
        sourceBtn.setBackground(new Background(new BackgroundFill(sourceNodeColor, null, null)));
        Button destBtn = new Button("终点");
        destBtn.setBackground(new Background(new BackgroundFill(destNodeColor, null, null)));
        Button pathFinderBtn = new Button("规划路径");
        pathFinderBtn.setBackground(new Background(new BackgroundFill(pathNodeColor, null, null)));
        Button resetBtn = new Button("重置");
        locationText = new Label();
        // 定义事件
        zoomInBtn.setOnAction(event -> mapCells.keySet().stream().forEach(cell -> {
            cell.setWidth(cell.getWidth() + 5);
            cell.setHeight(cell.getHeight() + 5);
        }));
        zoomOutBtn.setOnAction(event -> mapCells.keySet().stream().forEach(cell -> {
            cell.setWidth(cell.getWidth() - 5);
            cell.setHeight(cell.getHeight() - 5);
        }));
        sourceBtn.setOnAction(event -> {
            if (null != selectedNode) {
                if (null != sourceNode) {
                    sourceNode.setFill(cellDefaultColor);
                }
                sourceNode = selectedNode;
                sourceNode.setFill(sourceNodeColor);
                bottomText.setText("");
                selectedNode = null;
            } else {
                bottomText.setText("请选择一个点作为起点！！！");
            }
        });
        destBtn.setOnAction(event -> {
            if (null != selectedNode) {
                if (null != destNode) {
                    destNode.setFill(cellDefaultColor);
                }
                destNode = selectedNode;
                destNode.setFill(destNodeColor);
                selectedNode = null;
                bottomText.setText("");
            } else {
                bottomText.setText("请选择一个点作为终点！！！");
            }
        });
        pathFinderBtn.setOnAction(event -> {
            if (null != sourceNode && null != destNode) {
                Node sn = mapCells.get(sourceNode);
                Node dn = mapCells.get(destNode);
                // 起点/终点
                List<String> ss = Lists.newArrayList(sn.getPoint().getName(), dn.getPoint().getName());
                AStar aStar = new AStar.Builder()
                        .width(xWidth)
                        .height(yHeight)
                        .sourceNode(sn)
                        .destNode(dn)
                        .build();
                aStar.setBlocks(blocks);
                try {
                    Future<PathNodeList> future1 = executor.submit(new PathFinder(aStar, 0));
                    Future<PathNodeList> future2 = executor.submit(new PathFinder(aStar, 0.1));
                    Future<PathNodeList> future3 = executor.submit(new PathFinder(aStar, -0.1));
                    Queue<PathNodeList> pathNodeListQueue = Queues.newPriorityQueue();
                    pathNodeListQueue.add(future1.get());
                    pathNodeListQueue.add(future2.get());
                    pathNodeListQueue.add(future3.get());
                    log.info("*** 三条路径：{}", pathNodeListQueue);
                    if (!pathNodeListQueue.isEmpty()) {
                        PathNodeList path = pathNodeListQueue.poll();
                        log.info("*** 选择路径：{}", path);
                        aStar.setPath(path.getPoints());
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                int[][] map = aStar.getMap();
                for (int y = 0; y < aStar.getHeight(); y++) {
                    for (int x = 0; x < aStar.getWidth(); x++) {
                        if (map[y][x] == Constants.PATH) {
                            String xy = xy(x, y);
                            if (!ss.contains(xy)) {
                                xyCells.get(xy).setFill(pathNodeColor);
                            }
                        }
                    }
                }
            } else {
                bottomText.setText("起点或终点未选择，请确认！！！");
            }
        });
        resetBtn.setOnAction(event -> xyCells.forEach((k, v) -> {
            v.setFill(cellDefaultColor);
            if (blockMap.containsKey(k)) {
                v.setFill(PointNodeColor);
            }
        }));
        // 添加到 pane
        topPane.add(zoomInBtn, 0, 0);
        topPane.add(zoomOutBtn, 1, 0);
        topPane.add(sourceBtn, 2, 0);
        topPane.add(destBtn, 3, 0);
        topPane.add(pathFinderBtn, 4, 0);
        topPane.add(resetBtn, 5, 0);
        topPane.add(locationText, 6, 0);
    }

}
